﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace OkTool.Util
{
    public class NumberUtil
    {
        /// <summary>
        /// 是否为数字，支持包括：
        /// <list type="number">
        /// <item>10进制</item>
        /// <item>16进制数字（0x开头）</item>
        /// <item>科学计数法形式（1234E3）</item>
        /// <item>类型标识形式（123D）</item>
        /// <item>正负数标识形式（+123、-234）</item>
        /// </list>
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsNumber(string str)
        {
            if (string.IsNullOrWhiteSpace(str))
            {
                return false;
            }

            char[] chars = str.ToCharArray();
            int sz = chars.Length;
            bool hasExp = false;
            bool hasDecPoint = false;
            bool allowSigns = false;
            bool foundDigit = false;

            // Deal with any possible sign up front
            int start = (chars[0] == '-' || chars[0] == '+') ? 1 : 0;
            if (sz > start + 1)
            {
                if (chars[start] == '0' && (chars[start + 1] == 'x' || chars[start + 1] == 'X'))
                {
                    int ii = start + 2;
                    if (ii == sz)
                    {
                        return false; // str == "0x"
                    }

                    // Checking hex (it can't be anything else)
                    for (int iii=ii; iii < chars.Length; iii++)
                    {
                        if (!((chars[iii] >= '0' && chars[iii] <= '9') || (chars[iii] >= 'a' && chars[iii] <= 'f') || (chars[iii] >= 'A' && chars[iii] <= 'F')))
                        {
                            return false;
                        }
                    }
                    return true;
                }
            }

            sz--; // Don't want to loop to the last char, check it afterwards

            // For type qualifiers
            int i = start;
            // Loop to the next to last char or to the last char if we need another digit to
            // make a valid number (e.g. chars[0..5] = "1234E")
            while (i < sz || (i < sz + 1 && allowSigns && !foundDigit))
            {
                if (chars[i] >= '0' && chars[i] <= '9')
                {
                    foundDigit = true;
                    allowSigns = false;
                }
                else if (chars[i] == '.')
                {
                    if (hasDecPoint || hasExp)
                    {
                        // Two decimal points or dec in exponent
                        return false;
                    }
                    hasDecPoint = true;
                }
                else if (chars[i] == 'e' || chars[i] == 'E')
                {
                    // We've already taken care of hex.
                    if (hasExp)
                    {
                        // Two E's
                        return false;
                    }
                    if (!foundDigit)
                    {
                        return false;
                    }
                    hasExp = true;
                    allowSigns = true;
                }
                else if (chars[i] == '+' || chars[i] == '-')
                {
                    if (!allowSigns)
                    {
                        return false;
                    }
                    allowSigns = false;
                    foundDigit = false; // We need a digit after the E
                }
                else
                {
                    return false;
                }
                i++;
            }

            if (i < chars.Length)
            {
                if (chars[i] >= '0' && chars[i] <= '9')
                {
                    // No type qualifier, OK
                    return true;
                }
                if (chars[i] == 'e' || chars[i] == 'E')
                {
                    // Can't have an E at the last byte
                    return false;
                }
                if (chars[i] == '.')
                {
                    if (hasDecPoint || hasExp)
                    {
                        // Two decimal points or dec in exponent
                        return false;
                    }
                    // Single trailing decimal point after non-exponent is ok
                    return foundDigit;
                }
                if (!allowSigns && (chars[i] == 'd' || chars[i] == 'D' || chars[i] == 'f' || chars[i] == 'F'))
                {
                    return foundDigit;
                }
                if (chars[i] == 'l' || chars[i] == 'L')
                {
                    // Not allowing L with an exponent
                    return foundDigit && !hasExp;
                }
                // Last character is illegal
                return false;
            }

            // allowSigns is true iff the val ends in 'E'
            // found digit it to make sure weird stuff like '.' and '1E-' doesn't pass
            return false == allowSigns && foundDigit;
        }

        /// <summary>
        /// 判断string是否为整数
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static bool IsInt(string s)
        {
            if (StrUtil.IsBlank(s))
            {
                return false;
            }
            try
            {
                int.Parse(s);
            }
            catch (Exception)
            {

                return false;
            }

            return true;
        }

        /// <summary>
        /// 判断string是否为long
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static bool IsLong(string s)
        {
            if (StrUtil.IsBlank(s))
            {
                return false;
            }
            try
            {
                long.Parse(s);
            }
            catch (Exception)
            {

                return false;
            }

            return true;
        }

        /// <summary>
        /// 判断string是否为浮点数
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static bool IsDouble(string s)
        {
            if (StrUtil.IsBlank(s))
            {
                return false;
            }
            try
            {
                double.Parse(s);
            }
            catch (Exception)
            {

                return false;
            }

            return true;
        }

        /// <summary>
        /// 判断string是否为decimal
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static bool IsDecimal(string s)
        {
            if (StrUtil.IsBlank(s))
            {
                return false;
            }
            try
            {
                decimal.Parse(s);
            }
            catch (Exception)
            {

                return false;
            }

            return true;
        }

        /// <summary>
        /// 数字转decimal
        /// <para>null或者空白转换成0</para>
        /// </summary>
        /// <param name="numberStr"></param>
        /// <returns></returns>
        public static decimal ToDecimal(string numberStr)
        {
            return ConvertUtil.ToDecimal(numberStr, 0).Value;
        }

        /// <summary>
        /// 格式化金额输出,每三位用逗号分隔
        /// </summary>
        /// <param name="value">金额</param>
        /// <returns></returns>
        public static string FormatMoney(double value)
        {
            return value.ToString("#,0.00");
            //return value.ToString("###,##.##");

        }

        /// <summary>
        /// 格式化百分比,比如0.33,返回33.00%
        /// </summary>
        /// <param name="number">值</param>
        /// <param name="scale">保留小数位数</param>
        /// <returns>百分比</returns>
        public static string FormatPercent(double number, int scale)
        {
            NumberFormatInfo format = NumberFormatInfo.CurrentInfo.Clone() as NumberFormatInfo;
            format.PercentDecimalDigits = scale;
            return number.ToString("P", format);
        }

        /// <summary>
        /// 提供精确的加法运算
        /// </summary>
        /// <param name="v1">被加数</param>
        /// <param name="v2">加数</param>
        /// <returns></returns>
        public static double Add(float v1, float v2)
        {
            return (double)Add(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的加法运算
        /// </summary>
        /// <param name="v1">被加数</param>
        /// <param name="v2">加数</param>
        /// <returns></returns>
        public static double Add(float v1, double v2)
        {
            return (double)Add(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的加法运算
        /// </summary>
        /// <param name="v1">被加数</param>
        /// <param name="v2">加数</param>
        /// <returns></returns>
        public static double Add(double v1, float v2)
        {
            return (double)Add(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的加法运算
        /// </summary>
        /// <param name="v1">被加数</param>
        /// <param name="v2">加数</param>
        /// <returns></returns>
        public static double Add(double v1, double v2)
        {
            return (double)Add(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的加法运算
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static decimal Add(params string[] values)
        {
            if (ArrayUtil.IsEmpty(values))
            {
                return 0;
            }
            decimal result = ConvertUtil.ToDecimal(values[0], 0).Value;
            for (int i = 1; i < values.Length; i++)
            {
                result += ConvertUtil.ToDecimal(values[i], 0).Value;
            }
            return result;
        }

        /// <summary>
        /// 提供精确的加法运算
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static decimal Add(params decimal[] values)
        {
            if (ArrayUtil.IsEmpty(values))
            {
                return 0;
            }
            decimal result = values[0];
            for (int i = 1; i < values.Length; i++)
            {
                result += values[i];
            }
            return result;
        }

        /// <summary>
        /// 提供精确的减法运算
        /// </summary>
        /// <param name="v1">被减数</param>
        /// <param name="v2">减数</param>
        /// <returns></returns>
        public static double Sub(float v1, float v2)
        {
            return (double)Sub(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的减法运算
        /// </summary>
        /// <param name="v1">被减数</param>
        /// <param name="v2">减数</param>
        /// <returns></returns>
        public static double Sub(float v1, double v2)
        {
            return (double)Sub(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的减法运算
        /// </summary>
        /// <param name="v1">被减数</param>
        /// <param name="v2">减数</param>
        /// <returns></returns>
        public static double Sub(double v1, float v2)
        {
            return (double)Sub(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的减法运算
        /// </summary>
        /// <param name="v1">被减数</param>
        /// <param name="v2">减数</param>
        /// <returns></returns>
        public static double Sub(double v1, double v2)
        {
            return (double)Sub(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的减法运算
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static decimal Sub(params string[] values)
        {
            if (ArrayUtil.IsEmpty(values))
            {
                return 0;
            }
            decimal result = ConvertUtil.ToDecimal(values[0], 0).Value;
            for (int i = 1; i < values.Length; i++)
            {
                result -= ConvertUtil.ToDecimal(values[i], 0).Value;
            }
            return result;
        }

        /// <summary>
        /// 提供精确的减法运算
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static decimal Sub(params decimal[] values)
        {
            if (ArrayUtil.IsEmpty(values))
            {
                return 0;
            }
            decimal result = values[0];
            for (int i = 1; i < values.Length; i++)
            {
                result -= values[i];
            }
            return result;
        }

        /// <summary>
        /// 提供精确的乘法运算
        /// </summary>
        /// <param name="v1">被乘数</param>
        /// <param name="v2">乘数</param>
        /// <returns></returns>
        public static double Mul(float v1, float v2)
        {
            return (double)Mul(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的乘法运算
        /// </summary>
        /// <param name="v1">被乘数</param>
        /// <param name="v2">乘数</param>
        /// <returns></returns>
        public static double Mul(float v1, double v2)
        {
            return (double)Mul(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的乘法运算
        /// </summary>
        /// <param name="v1">被乘数</param>
        /// <param name="v2">乘数</param>
        /// <returns></returns>
        public static double Mul(double v1, float v2)
        {
            return (double)Mul(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的乘法运算
        /// </summary>
        /// <param name="v1">被乘数</param>
        /// <param name="v2">乘数</param>
        /// <returns></returns>
        public static double Mul(double v1, double v2)
        {
            return (double)Mul(v1.ToString(), v2.ToString());
        }

        /// <summary>
        /// 提供精确的乘法运算
        /// <para>如果传入多个值为null或者空,则返回0</para>
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static decimal Mul(params string[] values)
        {
            if (ArrayUtil.IsEmpty(values))
            {
                return 0;
            }
            decimal result = ConvertUtil.ToDecimal(values[0], 0).Value;
            for (int i = 1; i < values.Length; i++)
            {
                result *= ConvertUtil.ToDecimal(values[i], 0).Value;
            }
            return result;
        }
        /// <summary>
        /// 提供精确的乘法运算,忽略转换失败的
        /// <para>其中转换失败的不影响转换成功的相乘</para>
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static decimal MulIgnoreError(params string[] values)
        {
            if (ArrayUtil.IsEmpty(values))
            {
                return 0;
            }
            //进行转换过滤掉转换失败的
            var valuesNum = values.ToList().Select(x => ConvertUtil.ToDecimal(x)).Where(x => x.HasValue).Select(x => x.Value).ToList();

            decimal result = ConvertUtil.ToDecimal(valuesNum[0], 0).Value;
            for (int i = 1; i < valuesNum.Count; i++)
            {
                result *= ConvertUtil.ToDecimal(valuesNum[i], 0).Value;
            }
            return result;
        }

        /// <summary>
        /// 提供精确的乘法运算
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static decimal Mul(params decimal[] values)
        {
            if (ArrayUtil.IsEmpty(values))
            {
                return 0;
            }
            decimal result = values[0];
            for (int i = 1; i < values.Length; i++)
            {
                result *= values[i];
            }
            return result;
        }

        /// <summary>
        /// 提供(相对)精准的除法运算,当发生除不尽的情况时,由scale指定精确度
        /// </summary>
        /// <param name="v1">被除数</param>
        /// <param name="v2">除数,除数是0时返回0</param>
        /// <param name="scale">精确度,如果为负值,取绝对值</param>
        /// <param name="midpointRounding">ToEven=四舍六入五成双,AwayFromZero=四舍五入</param>
        /// <returns></returns>
        public static double Div(float v1, float v2, int scale, MidpointRounding midpointRounding)
        {
            return (double)Div(ConvertUtil.ToDecimal(v1).Value, ConvertUtil.ToDecimal(v2).Value, scale, midpointRounding);
        }

        /// <summary>
        /// 提供(相对)精准的除法运算,当发生除不尽的情况时,由scale指定精确度
        /// </summary>
        /// <param name="v1">被除数</param>
        /// <param name="v2">除数,除数是0时返回0</param>
        /// <param name="scale">精确度,如果为负值,取绝对值</param>
        /// <param name="midpointRounding">ToEven=四舍六入五成双,AwayFromZero=四舍五入</param>
        /// <returns></returns>
        public static double Div(double v1, double v2, int scale, MidpointRounding midpointRounding)
        {
            return (double)Div(ConvertUtil.ToDecimal(v1).Value, ConvertUtil.ToDecimal(v2).Value, scale, midpointRounding);
        }

        /// <summary>
        /// 提供(相对)精准的除法运算,当发生除不尽的情况时,由scale指定精确度
        /// </summary>
        /// <param name="v1">被除数</param>
        /// <param name="v2">除数,除数是0时返回0</param>
        /// <param name="scale">精确度,如果为负值,取绝对值</param>
        /// <param name="midpointRounding">ToEven=四舍六入五成双,AwayFromZero=四舍五入</param>
        /// <returns></returns>
        public static decimal Div(string v1, string v2, int scale, MidpointRounding midpointRounding)
        {

            return Div(ToDecimal(v1), ToDecimal(v2), scale, midpointRounding);
        }

        /// <summary>
        /// 提供(相对)精准的除运算,当发生除不尽的情况时,由scale指定精确度
        /// </summary>
        /// <param name="v1">被除数</param>
        /// <param name="v2">除数,除数是0时返回0</param>
        /// <param name="scale">精确度,如果为负值,取绝对值</param>
        /// <param name="midpointRounding">ToEven=四舍六入五成双,AwayFromZero=四舍五入</param>
        /// <returns></returns>
        public static decimal Div(decimal v1, decimal v2, int scale, MidpointRounding midpointRounding)
        {
            if (v2 == 0)
            {
                return 0;
            }

            if (scale < 0)
            {
                scale = -scale;
            }
            return Math.Round(v1 / v2, scale, midpointRounding);
        }

        /// <summary>
        /// 保留固定位数小数,默认四舍五入
        /// </summary>
        /// <param name="numberStr"></param>
        /// <param name="scale"></param>
        /// <param name="midpointRounding">ToEven=四舍六入五成双,AwayFromZero=四舍五入</param>
        /// <returns></returns>
        public static decimal Round(string numberStr, int scale, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
        {
            if (scale < 0)
            {
                scale = 0;
            }
            return Round(ToDecimal(numberStr), scale, midpointRounding);
        }

        /// <summary>
        /// 保留固定位数小数,默认四舍五入
        /// </summary>
        /// <param name="number"></param>
        /// <param name="scale"></param>
        /// <param name="midpointRounding">ToEven=四舍六入五成双,AwayFromZero=四舍五入</param>
        /// <returns></returns>
        public static decimal Round(decimal number, int scale, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
        {
            if (scale < 0)
            {
                scale = 0;
            }
            return Math.Round(number, scale, midpointRounding);
        }

        /// <summary>
        /// 生成不重复随机数 根据给定的最小数字和最大数字，以及随机数的个数，产生指定的不重复的数组
        /// </summary>
        /// <param name="begin">最小数字（包含该数）</param>
        /// <param name="end">最大数字（不包含该数）</param>
        /// <param name="size">指定产生随机数的个数</param>
        /// <returns>随机int数组</returns>
        public static int[] GenerateRandomNumber(int begin, int end, int size)
        {
            // 种子你可以随意生成，但不能重复
            int[] seed = ArrayUtil.Range(begin, end);
            return GenerateRandomNumber(begin, end, size, seed);
        }

        /// <summary>
        /// 生成不重复随机数 根据给定的最小数字和最大数字，以及随机数的个数，产生指定的不重复的数组
        /// </summary>
        /// <param name="begin">最小数字（包含该数）</param>
        /// <param name="end">最大数字（不包含该数）</param>
        /// <param name="size">指定产生随机数的个数</param>
        /// <param name="seed">种子，用于取随机数的int池</param>
        /// <returns>随机int数组</returns>
        public static int[] GenerateRandomNumber(int begin, int end, int size,int[]seed)
        {
            if (begin > end)
            {
                int temp = begin;
                begin = end;
                end = temp;
            }
            // 加入逻辑判断，确保begin<end并且size不能大于该表示范围
       
            int[] ranArr = new int[size];
            // 数量你可以自己定义。
            for (int i = 0; i < size; i++)
            {
                // 得到一个位置
                int j = RandomUtil.RandomInt(seed.Length - i);
                // 得到那个位置的数值
                ranArr[i] = seed[j];
                // 将最后一个未用的数字放到这里
                seed[j] = seed[seed.Length - 1 - i];
            }
            return ranArr;
        }
    }
}
